1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.escape;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.collect.ImmutableMap;
21  import com.google.common.escape.testing.EscaperAsserts;
22  
23  import junit.framework.TestCase;
24  
25  import java.io.IOException;
26  
27  /**
28   * @author David Beaumont
29   */
30  @GwtCompatible
31  public class EscapersTest extends TestCase {
32    public void testNullEscaper() throws IOException {
33      Escaper escaper = Escapers.nullEscaper();
34      EscaperAsserts.assertBasic(escaper);
35      String s = "\0\n\t\\az09~\uD800\uDC00\uFFFF";
36      assertEquals("null escaper should have no effect", s, escaper.escape(s));
37    }
38  
39    public void testBuilderInitialStateNoReplacement() {
40      // Unsafe characters aren't modified by default (unsafeReplacement == null).
41      Escaper escaper = Escapers.builder().setSafeRange('a', 'z').build();
42      assertEquals("The Quick Brown Fox", escaper.escape("The Quick Brown Fox"));
43    }
44  
45    public void testBuilderInitialStateNoneUnsafe() {
46      // No characters are unsafe by default (safeMin == 0, safeMax == 0xFFFF).
47      Escaper escaper = Escapers.builder().setUnsafeReplacement("X").build();
48      assertEquals("\0\uFFFF", escaper.escape("\0\uFFFF"));
49    }
50  
51    public void testBuilderRetainsState() {
52      // Setting a safe range and unsafe replacement works as expected.
53      Escapers.Builder builder = Escapers.builder();
54      builder.setSafeRange('a', 'z');
55      builder.setUnsafeReplacement("X");
56      assertEquals("XheXXuickXXrownXXoxX",
57          builder.build().escape("The Quick Brown Fox!"));
58      // Explicit replacements take priority over unsafe characters.
59      builder.addEscape(' ', "_");
60      builder.addEscape('!', "_");
61      assertEquals("Xhe_Xuick_Xrown_Xox_",
62          builder.build().escape("The Quick Brown Fox!"));
63      // Explicit replacements take priority over safe characters.
64      builder.setSafeRange(' ', '~');
65      assertEquals("The_Quick_Brown_Fox_",
66          builder.build().escape("The Quick Brown Fox!"));
67    }
68  
69    public void testBuilderCreatesIndependentEscapers() {
70      // Setup a simple builder and create the first escaper.
71      Escapers.Builder builder = Escapers.builder();
72      builder.setSafeRange('a', 'z');
73      builder.setUnsafeReplacement("X");
74      builder.addEscape(' ', "_");
75      Escaper first = builder.build();
76      // Modify one of the existing mappings before creating a new escaper.
77      builder.addEscape(' ', "-");
78      builder.addEscape('!', "$");
79      Escaper second = builder.build();
80      // This should have no effect on existing escapers.
81      builder.addEscape(' ', "*");
82  
83      // Test both escapers after modifying the builder.
84      assertEquals("Xhe_Xuick_Xrown_XoxX", first.escape("The Quick Brown Fox!"));
85      assertEquals("Xhe-Xuick-Xrown-Xox$", second.escape("The Quick Brown Fox!"));
86    }
87  
88    public void testAsUnicodeEscaper() throws IOException {
89      CharEscaper charEscaper = createSimpleCharEscaper(
90          ImmutableMap.<Character, char[]>builder()
91              .put('x', "<hello>".toCharArray())
92              .put('\uD800', "<hi>".toCharArray())
93              .put('\uDC00', "<lo>".toCharArray())
94              .build());
95      UnicodeEscaper unicodeEscaper = Escapers.asUnicodeEscaper(charEscaper);
96      EscaperAsserts.assertBasic(unicodeEscaper);
97      assertEquals("<hello><hi><lo>", charEscaper.escape("x\uD800\uDC00"));
98      assertEquals("<hello><hi><lo>", unicodeEscaper.escape("x\uD800\uDC00"));
99  
100     // Test that wrapped escapers acquire good Unicode semantics.
101     assertEquals("<hi><hello><lo>", charEscaper.escape("\uD800x\uDC00"));
102     try {
103       unicodeEscaper.escape("\uD800x\uDC00");
104       fail("should have failed for bad Unicode input");
105     } catch (IllegalArgumentException e) {
106       // pass
107     }
108     assertEquals("<lo><hi>", charEscaper.escape("\uDC00\uD800"));
109     try {
110       unicodeEscaper.escape("\uDC00\uD800");
111       fail("should have failed for bad Unicode input");
112     } catch (IllegalArgumentException e) {
113       // pass
114     }
115   }
116 
117   // A trival non-optimized escaper for testing.
118   private CharEscaper createSimpleCharEscaper(
119       final ImmutableMap<Character, char[]> replacementMap) {
120     return new CharEscaper() {
121       @Override protected char[] escape(char c) {
122         return replacementMap.get(c);
123       }
124     };
125   }
126 
127   // A trival non-optimized escaper for testing.
128   private UnicodeEscaper createSimpleUnicodeEscaper(
129       final ImmutableMap<Integer, char[]> replacementMap) {
130     return new UnicodeEscaper() {
131       @Override protected char[] escape(int cp) {
132         return replacementMap.get(cp);
133       }
134     };
135   }
136 }